Android 10 切换语言后无效 android多语言切换 您所在的位置:网站首页 Android updateconfiguration无效 Android 10 切换语言后无效 android多语言切换

Android 10 切换语言后无效 android多语言切换

2024-06-01 01:51| 来源: 网络整理| 查看: 265

android语言切换是在packages/apps/Settings/com/android/settings/LocalePicker.java的updateLocale()函数中调用.

/** * Requests the system to update the system locale. Note that the system looks halted * for a while during the Locale migration, so the caller need to take care of it. */ public static void updateLocale(Locale locale) { try { IActivityManager am = ActivityManagerNative.getDefault(); Configuration config = am.getConfiguration(); config.locale = locale; // indicate this isn't some passing default - the user wants this remembered config.userSetLocale = true; am.updateConfiguration(config); // Trigger the dirty bit for the Settings Provider. BackupManager.dataChanged("com.android.providers.settings"); } catch (RemoteException e) { // Intentionally left blank } }

从注释可以看出, 只要本地local改变就会调用该函数. 查看ActivityManagerNative的getDefault()可以看到, 该函数返回的是远程服务对象ActivityManagerServices.java在本地的一个代理. 最终调用的是ActivityManagerService.java中的updateConfiguration()函数.

public void updateConfiguration(Configuration values) { enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION, "updateConfiguration()"); synchronized(this) { if (values == null && mWindowManager != null) { // sentinel: fetch the current configuration from the window manager values = mWindowManager.computeNewConfiguration(); } if (mWindowManager != null) { mProcessList.applyDisplaySize(mWindowManager); } final long origId = Binder.clearCallingIdentity(); if (values != null) { Settings.System.clearConfiguration(values); } updateConfigurationLocked(values, null, false, false); Binder.restoreCallingIdentity(origId); } }

该函数, 首先进行的是权限的校验. 然后调用updateConfigurationLocked()函数.

/** * Do either or both things: (1) change the current configuration, and (2) * make sure the given activity is running with the (now) current * configuration. Returns true if the activity has been left running, or * false if starting is being destroyed to match the new * configuration. * @param persistent TODO */ public boolean updateConfigurationLocked(Configuration values, ActivityRecord starting, boolean persistent, boolean initLocale) { int changes = 0; boolean kept = true; if (values != null) { Configuration newConfig = new Configuration(mConfiguration); changes = newConfig.updateFrom(values); if (changes != 0) { if (DEBUG_SWITCH || DEBUG_CONFIGURATION) { Slog.i(TAG, "Updating configuration to: " + values); } EventLog.writeEvent(EventLogTags.CONFIGURATION_CHANGED, changes); if (values.locale != null && !initLocale) { saveLocaleLocked(values.locale, !values.locale.equals(mConfiguration.locale), values.userSetLocale, values.simSetLocale); } mConfigurationSeq++; if (mConfigurationSeq =0; i--) { ProcessRecord app = mLruProcesses.get(i); try { if (app.thread != null) { if (DEBUG_CONFIGURATION) Slog.v(TAG, "Sending to proc " + app.processName + " new config " + mConfiguration); app.thread.scheduleConfigurationChanged(configCopy); } } catch (Exception e) { } } Intent intent = new Intent(Intent.ACTION_CONFIGURATION_CHANGED); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_REPLACE_PENDING); broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null, false, false, MY_PID, Process.SYSTEM_UID); if ((changes&ActivityInfo.CONFIG_LOCALE) != 0) { broadcastIntentLocked(null, null, new Intent(Intent.ACTION_LOCALE_CHANGED), null, null, 0, null, null, null, false, false, MY_PID, Process.SYSTEM_UID); } } } if (changes != 0 && starting == null) { // If the configuration changed, and the caller is not already // in the process of starting an activity, then find the top // activity to check if its configuration needs to change. starting = mMainStack.topRunningActivityLocked(null); } if (starting != null) { kept = mMainStack.ensureActivityConfigurationLocked(starting, changes); // And we need to make sure at this point that all other activities // are made visible with the correct configuration. mMainStack.ensureActivitiesVisibleLocked(starting, changes); } if (values != null && mWindowManager != null) { mWindowManager.setNewConfiguration(mConfiguration); } return kept; }

整个语言切换就在这个函数中完成. 咋一看似乎没感觉到该函数做了哪些事情. 我们首先来看注释:Do either or both things: (1) change the current configuration, and (2)

make sure the given activity is running with the (now) current. configuration大概意思是: 这个函数做了两件事情. (1). 改变当前的configuration. 意思就是让改变的configuration更新到当前configuration. (2) 确保所有正在运行的activity都能更新改变后的configuration.(这点是关键.) . 我们按照这个思路看看android是如何更新configuration. 查看代码 , 首先看到 这个函数首先判断values是否为空, 这里values肯定不为空的, 然后changes = newConfig.updateFrom(values); 我们看看updateFrom做了什么操作.

/** * Copy the fields from delta into this Configuration object, keeping * track of which ones have changed. Any undefined fields in * delta are ignored and not copied in to the current * Configuration. * @return Returns a bit mask of the changed fields, as per * {@link #diff}. */ public int updateFrom(Configuration delta) { int changed = 0; ... if (delta.locale != null && (locale == null || !locale.equals(delta.locale))) { changed |= ActivityInfo.CONFIG_LOCALE; locale = delta.locale != null ? (Locale) delta.locale.clone() : null; textLayoutDirection = LocaleUtil.getLayoutDirectionFromLocale(locale); } if (delta.userSetLocale && (!userSetLocale || ((changed & ActivityInfo.CONFIG_LOCALE) != 0))) { userSetLocale = true; changed |= ActivityInfo.CONFIG_LOCALE; } ... return changed; }

因为语言改变了, 那么 (!locale.equals(delta.locale)) 是true. changed 大于0, 然后return changed. 回到ActivityManagerService.java的updateConfigurationLocked函数, 因为changed不为0 , 所以走if这个流程. 继续看代码

for (int i=mLruProcesses.size()-1; i>=0; i--) { ProcessRecord app = mLruProcesses.get(i); try { if (app.thread != null) { if (DEBUG_CONFIGURATION) Slog.v(TAG, "Sending to proc " + app.processName + " new config " + mConfiguration); app.thread.scheduleConfigurationChanged(configCopy); } } catch (Exception e) { } }

首先看到的是mLurProcesses 是ArrayList类型. LRU : Least Recently Used保存所有运行过的进程. ProcessRecord进程类, 一个apk文件运行时会对应一个进程. app.thread. 此处的thread代表的是ApplicationThreadNative.java类型. 然后调用其scheduleConfigurationChanged(); 查看该函数

public final void scheduleConfigurationChanged(Configuration config) throws RemoteException { Parcel data = Parcel.obtain(); data.writeInterfaceToken(IApplicationThread.descriptor); config.writeToParcel(data, 0); mRemote.transact(SCHEDULE_CONFIGURATION_CHANGED_TRANSACTION, data, null, IBinder.FLAG_ONEWAY); data.recycle(); }

又是通过binder调用, 所以 , binder在android中是一个很重要的概念. 此处远程调用的是ActivityThread.java中的私有内部内ApplicationThread

private class ApplicationThread extends ApplicationThreadNative { private static final String HEAP_COLUMN = "%13s %8s %8s %8s %8s %8s %8s"; private static final String ONE_COUNT_COLUMN = "%21s %8d"; private static final String TWO_COUNT_COLUMNS = "%21s %8d %21s %8d"; private static final String TWO_COUNT_COLUMNS_DB = "%21s %8d %21s %8d"; private static final String DB_INFO_FORMAT = " %8s %8s %14s %14s %s"; ... public void scheduleConfigurationChanged(Configuration config) { updatePendingConfiguration(config); queueOrSendMessage(H.CONFIGURATION_CHANGED, config); } ... }

而ApplicationThread中的handler的CONFIGURATION_CHANGED是调用handleConfigurationChanged()

final void handleConfigurationChanged(Configuration config, CompatibilityInfo compat) { ArrayList callbacks = null; ... ... applyConfigurationToResourcesLocked(config, compat); ... callbacks = collectComponentCallbacksLocked(false, config); ... if (callbacks != null) { final int N = callbacks.size(); for (int i=0; i


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有